package com.outdd.vaietool.common.utils;


import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.*;

/**
 * 重组java方法
 *
 * @author vaie
 */
@Slf4j
public class JavaReflectUtil {

    /**
     * 排除的父类
     */
    static List<Class<?>> excludedSuperClazz = new ArrayList<Class<?>>(
            Arrays.asList(
                    Object.class
            )
    );


    /**
     * 获取父类的所有字段
     *
     * @param tableFields
     * @param clazz
     * @return
     */
    private static Field[] getSuperClassFields(Field[] tableFields, Class<?> clazz) {
        return getSuperClassFields(tableFields, clazz, excludedSuperClazz);
    }

    /**
     * 获取父类的所有字段
     *
     * @param tableFields
     * @param clazz
     * @return
     */
    private static Field[] getSuperClassFields(Field[] tableFields, Class<?> clazz, List<Class<?>> excludedSuperClazzs) {
        Class<?> superClazz = clazz.getSuperclass();
        if (
                (ObjectUtils.isNotEmpty(excludedSuperClazzs)
                        && excludedSuperClazzs.contains(superClazz))
                        || Object.class.equals(superClazz)
                        || ObjectUtils.isEmpty(superClazz)
        ) {
            return tableFields;
        }

        Field[] tableSuperFields = superClazz.getDeclaredFields();

        Field[] c = new Field[tableFields.length + tableSuperFields.length];
        System.arraycopy(tableFields, 0, c, 0, tableFields.length);
        System.arraycopy(tableSuperFields, 0, c, tableFields.length, tableSuperFields.length);
        getSuperClassFields(c, superClazz);
        return c;
    }


    /**
     * 获取类的所有字段
     *
     * @param clazz
     * @return
     */
    public static Field[] getFields(Class<?> clazz) {

        return getFields(clazz, excludedSuperClazz);
    }

    /**
     * 获取类的所有字段转换成List集合
     *
     * @param clazz
     * @return
     */
    public static List<String> getFieldsToList(Class<?> clazz) {
        ArrayList<String> list = new ArrayList<>();

        Field[] fields = getFields(clazz);

        for (Field field : fields) {
            list.add(field.getName());
        }

        return list;
    }

    /**
     * 获取类的所有字段转换成List集合
     *
     * @param clazz
     * @return
     */
    public static Map<String, Class> getFieldsToMap(Class<?> clazz) {
        HashMap<String, Class> map = new HashMap<>();

        Field[] fields = getFields(clazz);

        for (Field field : fields) {
            map.put(field.getName(), field.getType());
        }

        return map;
    }

    /**
     * 获取类的所有字段转换成List集合
     *
     * @param clazz
     * @return
     */
    public static Map<String, Field> getFieldsToMapField(Class<?> clazz) {
        HashMap<String, Field> map = new HashMap<>();

        Field[] fields = getFields(clazz);

        for (Field field : fields) {
            map.put(field.getName(), field);
        }

        return map;
    }


    /**
     * 获取类的所有字段
     * @param clazz 当前类
     * @param excludedSuperClazzs 需要排除的父类
     * @return
     */
    public static Field[] getFields(Class<?> clazz, List<Class<?>> excludedSuperClazzs) {

        Field[] tableFields = clazz.getDeclaredFields();

        Class<?> superClazz = clazz.getSuperclass();

        if (Object.class.equals(superClazz)
                || ObjectUtils.isEmpty(superClazz)
        ) {
            return tableFields;
        }
        Field[] tableSuperFields = superClazz.getDeclaredFields();
        Field[] superFields = new Field[tableFields.length + tableSuperFields.length];

        System.arraycopy(tableFields, 0, superFields, 0, tableFields.length);
        System.arraycopy(tableSuperFields, 0, superFields, tableFields.length, tableSuperFields.length);
        tableFields = getSuperClassFields(superFields, superClazz, excludedSuperClazzs);

        return tableFields;
    }


    /**
     * 获取对象所有字段
     *
     * @param clazz
     * @return
     */
    public static Field[] classAllfield(Class<?> clazz) {
        Field[] tableFields = clazz.getDeclaredFields();
        Class<?> superClazz = clazz.getSuperclass();

        if (!superClazz.equals(Object.class)) {

            tableFields = getFields(clazz);

        }

        return tableFields;
    }


    /**
     * 反射set方法
     *
     * @param obj
     * @param field
     * @param args
     * @return
     */
    public static Object invokeSetMethod(Object obj, Field field, Object args) {
        return invokeSetMethod(obj, field.getName(), args);
    }

    /**
     * 反射get方法
     *
     * @param obj
     * @param field
     * @return
     */
    public static Object invokeGetMethod(Object obj, Field field) {
        return invokeGetMethod(obj, field.getName());
    }


    /**
     * 反射set方法
     *
     * @param obj
     * @param fieldName
     * @param args
     * @return
     */
    public static Object invokeSetMethod(Object obj, String fieldName, Object args) {
        Object fieldValue = null;

        if (ObjectUtils.isEmpty(obj)) {
            return null;
        }
        try {
            PropertyDescriptor pd = new PropertyDescriptor(fieldName, obj.getClass());
            //获得set方法;
            Method method = pd.getWriteMethod();
            fieldValue = ReflectionUtils.invokeMethod(method, obj, args);
        } catch (Exception e) {
            fieldValue = getSetMethod(obj, fieldName, args);
        }
        return fieldValue;
    }

    /**
     * 反射set方法
     *
     * @param obj
     * @param fieldName
     * @param args
     * @return
     */
    public static Object getSetMethod(Object obj, String fieldName, Object args) {
        if (ObjectUtils.isEmpty(obj)) {
            return null;
        }
        Object fieldValue = null;
        try {
            Method[] methods = obj.getClass().getMethods();
            for (Method method : methods) {
                if (("set" + fieldName).toLowerCase().equals(method.getName().toLowerCase())) {

                    fieldValue = method.invoke(obj, args);

                }
            }
        } catch (Exception e) {
            log.error("获取对象方法时发生错误 obj:{},fieldName:{}", obj, fieldName, e);
        }
        return fieldValue;
    }

    /**
     * 反射get方法
     *
     * @param obj
     * @param fieldName
     * @return
     */
    public static Object invokeGetMethod(Object obj, String fieldName) {
        Object fieldValue = null;
        if (ObjectUtils.isEmpty(obj)) {
            return null;
        }
        try {
            PropertyDescriptor pd = new PropertyDescriptor(fieldName, obj.getClass());
            //获得get方法;
            Method method = pd.getReadMethod();

            if (obj != null) {
                fieldValue = ReflectionUtils.invokeMethod(method, obj);
            }

        } catch (Exception e) {
            fieldValue = getGetMethod(obj, fieldName);
        }
        return fieldValue;
    }

    /**
     * 反射get方法
     *
     * @param obj
     * @param fieldName
     * @return
     */
    public static Object getGetMethod(Object obj, String fieldName) {
        Object fieldValue = null;
        Method[] methods = obj.getClass().getMethods();
        for (Method method : methods) {
            if (("get" + fieldName).toLowerCase().equals(method.getName().toLowerCase())) {
                try {
                    fieldValue = method.invoke(obj);
                } catch (Exception e) {
                    log.error("获取对象方法时发生错误", e);
                }
            }
        }

        return fieldValue;
    }






    /**
     * 获取注解的值
     *
     * @param annotation 注解类
     * @param property   注解字段字符串
     * @return
     */
    public static Object getAnnotationValue(Annotation annotation, String property) {
        Object result = null;
        if (annotation != null) {
            //获取被代理的对象
            InvocationHandler invo = Proxy.getInvocationHandler(annotation);
            Map map = (Map) getFieldValue(invo, "memberValues");
            if (map != null) {
                result = map.get(property);
            }
        }
        return result;
    }

    /**
     * 辅助获取注解的值
     *
     * @param object
     * @param property
     * @param <T>
     * @return
     */
    public static <T> Object getFieldValue(T object, String property) {
        if (object != null && property != null) {
            Class<T> currClass = (Class<T>) object.getClass();

            try {
                Field field = currClass.getDeclaredField(property);
                field.setAccessible(true);
                return field.get(object);
            } catch (NoSuchFieldException e) {
                throw new IllegalArgumentException(currClass + " has no property: " + property);
            } catch (IllegalArgumentException e) {
                throw e;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * 获取当前类的所有父类
     *
     * @param calzz
     * @return
     */
    public static List<Class<?>> getsuperClass(Class<?> calzz) {
        List<Class<?>> listSuperClass = new ArrayList<Class<?>>();
        Class<?> superclass = calzz.getSuperclass();
        while (superclass != null) {
            if (superclass.getName().equals("java.lang.Object")) {
                break;
            }
            listSuperClass.add(superclass);
            superclass = superclass.getSuperclass();
        }
        return listSuperClass;
    }


    /**
     * 判断当前class父类是否包含对象
     *
     * @param calzz
     * @param zzz
     * @return
     */
    public static boolean isObjectSupConObj(Class<?> calzz, Class<?>... zzz) {
        List<Class<?>> classes = getsuperClass(calzz);

        boolean ac = false;
        for (Class<?> aClass : zzz) {
            if (classes.contains(aClass)) {
                return true;
            }
        }
        return ac;
    }


}
